package com.njmaomao.gj.pyot.service;

import com.njmaomao.gj.pyot.common.ResponseCode;
import com.njmaomao.gj.pyot.common.bean.dto.*;
import com.njmaomao.gj.pyot.common.exception.SystemBizFail;
import com.njmaomao.gj.pyot.config.Bizs;
import com.njmaomao.gj.pyot.domain.wechat.*;
import com.njmaomao.gj.pyot.domain.wechat.wechatapi.WechatApiService;
import com.njmaomao.gj.pyot.domain.wechat.wechatapi.orderquery.OrderQueryReq;
import com.njmaomao.gj.pyot.domain.wechat.wechatapi.orderquery.OrderQueryResp;
import com.njmaomao.gj.pyot.domain.wechat.wechatapi.refund.RefundReq;
import com.njmaomao.gj.pyot.domain.wechat.wechatapi.refund.RefundResp;
import com.njmaomao.gj.pyot.domain.wechat.wechatapi.refundquery.RefundQueryReq;
import com.njmaomao.gj.pyot.domain.wechat.wechatapi.refundquery.RefundQueryResp;
import com.njmaomao.gj.pyot.domain.wechat.wechatapi.unifiedorder.UnifiedorderReq;
import com.njmaomao.gj.pyot.domain.wechat.wechatapi.unifiedorder.UnifiedorderResp;
import com.njmaomao.gj.pyot.domain.model.PayRecord;
import com.njmaomao.gj.pyot.domain.model.RefundRecord;
import com.njmaomao.gj.pyot.repository.PayRecordRepository;
import com.njmaomao.gj.pyot.repository.RefundRecordRepository;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.List;
import java.util.Optional;

@Component
public class WechatAppService {

    @Autowired
    private WechatApiService wechatApiService;

    @Autowired
    private PayRecordRepository payRecordRepository;

    @Autowired
    private RefundRecordRepository refundRecordRepository;

    @Autowired
    private PayConfigFactory payConfigFactory;

    @Autowired
    private OrderFactory orderFactory;

    @Autowired
    private WechatNotificationAppService wechatNotificationAppService;

    private Logger log = LoggerFactory.getLogger(WechatAppService.class);

    public WechatJsPayResponse jsPay(boolean isSandbox, String appId, String orderCode, Integer orderType, Integer totalFee, String openId, String fromSystem, String callbackUrl, String orderDesc, Long customerId) {

        PayConfig payConfig = payConfigFactory.get(appId, Optional.empty(), isSandbox);

        PaymentOrder order = orderFactory.getOrder(orderCode, orderType, customerId, totalFee, orderDesc);
        SystemBizFail.whenIf(!order.isAllowPay(), ResponseCode.PAYTO.ORDER_NOT_ALLOW_PAY);

        UnifiedorderReq unifiedorderReq = UnifiedorderReq.build(order, payConfig, openId, "JSAPI");

        UnifiedorderResp unifiedorderResp = wechatApiService.unifiedorder(isSandbox, unifiedorderReq);

        if(!unifiedorderResp.isBizSuccess()) {
            return new WechatJsPayResponse(unifiedorderResp.getResultCode(), unifiedorderResp.getErrCode(), unifiedorderResp.getErrCodeDes());
        }

        initPayRecord(order, unifiedorderReq, fromSystem, callbackUrl, customerId);

        return Assembler.wechatJsPayResponse(buildJsRequest(unifiedorderResp, payConfig));
    }

    private WechatPayJsRequest buildJsRequest(UnifiedorderResp unifiedorderResp, PayConfig payConfig) {
        WechatPayJsRequest wechatPayJsRequest = new WechatPayJsRequest();
        wechatPayJsRequest.setAppId(payConfig.getAppid());
        wechatPayJsRequest.setTimeStamp(Bizs.generateTimestamp());
        wechatPayJsRequest.setNonceStr(Bizs.generateNonceStr());
        wechatPayJsRequest.setPackageStr("prepay_id=" + unifiedorderResp.getPrepayId());
        wechatPayJsRequest.setSignType(Bizs.SIGN_TYPE);
        wechatPayJsRequest.setPaySign(Bizs.getMd5Sign(wechatPayJsRequest, payConfig.getKey()));
        return wechatPayJsRequest;
    }

    public WechatAppPayResponse appPay(boolean isSandbox, String appId, String orderCode, Integer orderType, Integer totalFee, String openId, String fromSystem, String callbackUrl, String orderDesc, Long customerId) {

        PayConfig payConfig = payConfigFactory.get(appId, Optional.empty(), isSandbox);

        PaymentOrder order = orderFactory.getOrder(orderCode, orderType, customerId, totalFee, orderDesc);
        SystemBizFail.whenIf(!order.isAllowPay(), ResponseCode.PAYTO.ORDER_NOT_ALLOW_PAY);

        UnifiedorderReq unifiedorderReq = UnifiedorderReq.build(order, payConfig, openId, "APP");

        UnifiedorderResp unifiedorderResp = wechatApiService.unifiedorder(isSandbox, unifiedorderReq);

        if(!unifiedorderResp.isBizSuccess()) {
            return new WechatAppPayResponse(unifiedorderResp.getResultCode(), unifiedorderResp.getErrCode(), unifiedorderResp.getErrCodeDes());
        }

        initPayRecord(order, unifiedorderReq, fromSystem, callbackUrl, customerId);

        return Assembler.wechatAppPayResponse(buildAppRequest(unifiedorderResp, payConfig));
    }

    private WechatPayAppRequest buildAppRequest(UnifiedorderResp unifiedorderResp, PayConfig payConfig) {
        WechatPayAppRequest wechatPayAppRequest = new WechatPayAppRequest();
        wechatPayAppRequest.setAppid(payConfig.getAppid());
        wechatPayAppRequest.setPartnerid(unifiedorderResp.getMchId());
        wechatPayAppRequest.setPrepayid(unifiedorderResp.getPrepayId());
        wechatPayAppRequest.setPackageStr("Sign=WXPay");
        wechatPayAppRequest.setNoncestr(Bizs.generateNonceStr());
        wechatPayAppRequest.setTimestamp(Bizs.generateTimestamp());
        wechatPayAppRequest.setSign(Bizs.getMd5Sign(wechatPayAppRequest, payConfig.getKey()));
        return wechatPayAppRequest;
    }

    private PayRecord initPayRecord(PaymentOrder order, UnifiedorderReq unifiedorderReq, String fromSystem, String callbackUrl, Long customerId) {
        PayRecord payRecord = payRecordRepository.findByOrderCode(order.orderCode());
        if(payRecord == null) {
            payRecord = new PayRecord();
        }
        payRecord.setCustomerId(customerId);
        payRecord.setCurrency(unifiedorderReq.getFeeType());
        payRecord.setAppId(unifiedorderReq.getAppid());
        payRecord.setUserId(unifiedorderReq.getOpenid());
        payRecord.setCreateTime(new Date());
        payRecord.setMchId(unifiedorderReq.getMchId());
        payRecord.setCallbackUrl(callbackUrl);
        payRecord.setOrderCode(unifiedorderReq.getOutTradeNo());
        payRecord.setTotalAmount(unifiedorderReq.getTotalFee());
        payRecord.setPayStyle(PayStyle.WEIXIN.getValue());
        payRecord.setFromSystem(fromSystem);
        payRecord.setStatus(PayRecord.Status.PAYING.getStatus());
        payRecord.setOrderType(order.orderType().getType());
        payRecord.setPayMode(unifiedorderReq.getTradeType());
        return payRecordRepository.save(payRecord);
    }

    public OrderQueryRespDTO queryOrder(boolean isSandbox, String orderCode) {
        PayRecord payRecord = payRecordRepository.findByOrderCode(orderCode);
        SystemBizFail.whenIf(payRecord == null, ResponseCode.PAYTO.UNKNOW_ORDER);
        PayConfig payConfig = payConfigFactory.get(payRecord.getAppId(), Optional.ofNullable(payRecord.getMchId()), isSandbox);
        OrderQueryReq orderQueryReq = OrderQueryReq.build(payConfig, orderCode);
        OrderQueryResp orderQueryResp = wechatApiService.orderquery(isSandbox, orderQueryReq);
        if(!orderQueryResp.isBizSuccess()) {
            return new OrderQueryRespDTO(orderQueryResp.getResultCode(), orderQueryResp.getErrCode(), orderQueryResp.getErrCodeDes());
        }
        if(orderQueryResp.paidSuccess()) {
            try {
                wechatNotificationAppService.paySuccess(orderQueryResp.getOutTradeNo(), orderQueryResp.getTotalFee(), orderQueryResp.getOpenid(), orderQueryResp.getTimeEndForDate(), orderQueryResp.getTransactionId());
            } catch (Exception e) {
                log.error("微信支付|查询订单支付成功|更新状态失败" + e.getMessage(), e);
            }
        }
        return Assembler.orderQueryRespDTO(orderQueryResp);
    }

    public RefundRespDTO refundOrder(boolean isSandbox, String tradeNo, String refundOrderCode, Integer refundFee, String oderCode, String operator, String callbackUrl, String fromSystem, String refundDesc) {

        SystemBizFail.whenIf(StringUtils.isBlank(tradeNo) && StringUtils.isBlank(oderCode), ResponseCode.PAYTO.UNKNOW_ORDER);
        SystemBizFail.whenIf(refundRecordRepository.findByRefundNo(refundOrderCode) != null, ResponseCode.PAYTO.ORDER_REFUNDED);

        PayRecord payRecord;

        if(StringUtils.isNotBlank(tradeNo)) {
            payRecord = payRecordRepository.findByTradeNo(tradeNo);
        } else {
            payRecord = payRecordRepository.findByOrderCode(oderCode);
        }

        SystemBizFail.whenIf(payRecord == null, ResponseCode.PAYTO.UNKNOW_ORDER);


        PayConfig payConfig = payConfigFactory.get(payRecord.getAppId(), Optional.ofNullable(payRecord.getMchId()), isSandbox);
        RefundReq refundReq = RefundReq.build(payConfig, tradeNo, refundOrderCode, refundFee, payRecord);
        RefundResp refundResp = wechatApiService.refund(isSandbox, refundReq);

        if(!refundResp.isBizSuccess()) {
            return new RefundRespDTO(refundResp.getResultCode(), refundResp.getErrCode(), refundResp.getErrCodeDes());
        }

        RefundRecord refundRecord = newRefundRecord(operator, refundDesc,  callbackUrl, fromSystem, refundReq, refundResp);
        refundRecordRepository.save(refundRecord);

        return Assembler.refundRespDTO(refundResp);
    }

    private RefundRecord newRefundRecord(String operator, String refundDesc, String callbackUrl, String fromSystem, RefundReq refundReq, RefundResp refundResp) {
        RefundRecord refundRecord = new RefundRecord();
        refundRecord.setRefundCurrency(refundReq.getRefundFeeType());
        refundRecord.setCreateTime(new Date());
        refundRecord.setOperator(operator);
        refundRecord.setOrderCode(refundResp.getOutTradeNo());
        refundRecord.setTotalAmount(refundReq.getTotalFee());
        refundRecord.setRefundAmount(refundResp.getRefundFee());
        refundRecord.setRefundNo(refundResp.getOutRefundNo());
        refundRecord.setRefundDesc(refundDesc);
        refundRecord.setRefundId(refundResp.getRefundId());
        refundRecord.setStatus(RefundRecord.Status.REFUNDING.getStatus());
        refundRecord.setFromSystem(fromSystem);
        refundRecord.setCallbackUrl(callbackUrl);
        return refundRecord;
    }

    public RefundQueryRespDTO refundQuery(boolean isSandbox, String refundOrderCode) {

        RefundRecord refundRecord = refundRecordRepository.findByRefundNo(refundOrderCode);
        SystemBizFail.whenIf(refundRecord == null, ResponseCode.PAYTO.UNKNOW_ORDER);

        PayRecord payRecord = payRecordRepository.findByOrderCode(refundRecord.getOrderCode());

        PayConfig payConfig = payConfigFactory.get(payRecord.getAppId(), Optional.ofNullable(payRecord.getMchId()), isSandbox);

        RefundQueryReq refundQueryReq = RefundQueryReq.build(payConfig, refundOrderCode);
        RefundQueryResp refundQueryResp = wechatApiService.refundquery(isSandbox, refundQueryReq);
        if(!refundQueryResp.isBizSuccess()) {
            return new RefundQueryRespDTO(refundQueryResp.getResultCode(), refundQueryResp.getErrCode(), refundQueryResp.getErrCodeDes());
        }
        return Assembler.refundQueryRespDTO(refundQueryResp);
    }

    public CustomerPaidOrderRespDTO customerPaidOrder(Long customerId) {
        List<PayRecord> payRecords = payRecordRepository.findByCustomerIdAndStatus(customerId, PayRecord.Status.PAYED.getStatus());
        return Assembler.customerPaidOrderRespDTO(payRecords);
    }
}
